"use strict";
const http = require("http");
const fs = require("fs");
const fsp = fs.promises;

/**
 *解析body数据的消息头
 *
 * @param {*} data
 */
function parseBodyHeaders(data) {
    let headers = {};
    let dsplit = data.split("\r\n").filter((p) => p.length > 0); //filter()的作用是过滤序列
    let ind;
    let k;
    for (let d of dsplit) {
        ind = d.indexOf(":");
        if (ind <= 0) continue;
        k = d.substring(0, ind).toLowerCase();
        headers[k] = d.substring(ind + 1).trim(); //trim()方法用于去掉字符串首尾空格
    }

    let cpd = headers["content-disposition"];

    //按照;分割成数组，第一个元素form-data去掉
    let clist = cpd.split(";").splice(1);
    let name, filename;
    for (let a of clist) {
        ind = a.indexOf('filename="');
        if (ind >= 0) {
            filename = a.substring(ind + 10, a.length - 1);
            //ind+10：指filename="的长度
        } else {
            name = a.substring(6, a.length - 1);
        }
    }
    return {
        headers,
        name,
        filename,
    };
}

/**
 *
 *
 * @param {Buffer} bodyData 原始的body数据
 * @param {Object} reqHeader req.headers
 */

function parseBody(bodyData, reqHeaders) {
    //content-type:multipart/form-data;boundary=-----34294329
    let ctype = reqHeaders["content-type"]; //标准格式

    // 拿到分界线boundary
    let bdy = ctype.substring(ctype.indexOf("=") + 1);

    // !数据用于分割文件的分界线
    //两次\r\n可以通过一次查找知道消息头和消息体
    // bdy = `--${bdy}\r\n`;

    let crlf_bdy = `\r\n--${bdy}`;

    //从偏移crlf_bdy的长度开始查找下一个分界限的位置
    let data_end_index = bodyData.indexOf(crlf_bdy, crlf_bdy.length);
    //从下标crlf_bdy.length处开始查找，第一个crlf_bdy开始出现的地方

    //body消息数据中header部分的最后索引
    let header_end_index = bodyData.indexOf("\r\n\r\n", crlf_bdy.length);
    let header_data = bodyData.toString(
        "utf-8",
        crlf_bdy.length,
        header_end_index
    );

    //解析body数据中的消息头
    let headers = parseBodyHeaders(header_data);

    let fileinfo = {
        start: header_end_index + 4, //由于连续的两个\r\n
        end: data_end_index,
    };
    fileinfo.length = fileinfo.end - fileinfo.start;
    return {
        name: headers.name,
        filename: headers.filename,
        headers: headers.headers,
        start: fileinfo.start,
        end: fileinfo.end,
        length: fileinfo.length,
    };
}

function parseExtName(filename) {
    if (filename.length < 2) {
        return "";
    }
    let namesplit = filename.split(".").filter((p) => p.length > 0);
    if (namesplit.length < 2) {
        return "";
    }
    return `.${namesplit[namesplit.length - 1]}`;
}

let routerTable = {
    GET: {
        "/upload": async (ctx) => {
            let stm = fs.createReadStream("./upload.html");
            stm.pipe(ctx.res);
            stm.on("end", () => {
                ctx.end();
            });
        },
    },
    POST: {
        "/upload": async (ctx) => {
            console.log(ctx.files);
            let finfo = ctx.files;

            let filename =
                `${Date.now()}-` +
                `${parseInt(Math.random() * 100000) + 100000}` +
                parseExtName(finfo.filename);

            let fh = await fsp.open(filename, "w+");
            let fret = await fh.write(
                ctx.rawBody,
                finfo.start,
                finfo.length,
                0
            );

            // 关闭打开的文件
            fh.close();

            // 返回写入的字节数,string或者是buffer类型，不能是其他类型
            ctx.end(`${fret.bytesWritten}`);
        },
    },
};

/**----------------------------START SERVER------------------------------------ */

let serv = http.createServer();
serv.on("request", (req, res) => {
    let rm = routerTable[req.method];

    // 根据？切分字符串
    let usplit = req.url.split("?");

    let pathname = usplit[0];
    let querystring = usplit[1] || ""; //如果没有问号的话，把空字符串传给querystring

    if (!rm || !rm[pathname]) {
        res.statusCode = 404;
        res.end("page not found");
        return;
    }

    // ctx is context,创建请求上下文对象
    let ctx = {
        req: req,
        res: res,
        method: req.method,
        headers: req.headers,
        end: (data, encoding = "utf-8") => {
            res.end(data, encoding);
        },
        write: (data, encoding = "utf-8") => {
            res.write(data, encoding);
        },
        path: pathname,
        querystring: querystring,
        rawBody: null,
    };

    let bufferList = [];
    let totalLength = 0;

    /* 能够提交body数据的请求:POST PUT PATCH   DELETE （前三个是必须的，最后一个是可选的）
    不能提交：GET HEAD TRACE OPTIONS*/
    if ("PD".indexOf(req.method[0] >= 0)) {
        req.on("data", (chunk) => {
            totalLength += chunk.length;
            bufferList.push(chunk);
        });
    } else {
        req.on("data", (chunk) => {
            res.statusCode = 400;
            res.end();
        });
    }
    req.on("end", () => {
        if (bufferList.length > 0) {
            ctx.rawBody = Buffer.concat(bufferList, totalLength);
            bufferList = [];
        }

        let ctype = ctx.headers["content-type"] || "";
        if (ctx.rawBody && ctype.indexOf("multipart/form-data") === 0) {
            ctx.files = parseBody(ctx.rawBody, ctx.headers);
        }

        rm[pathname](ctx);
    });
});

serv.listen(3456);
